﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Configuration;

namespace WebPlayer
{
    public partial class Play : System.Web.UI.Page
    {
        private PlayerHandler _mPlayer;
        private OutputBuffer _mBuffer;
        private string _mGameId;
        private Dictionary<string, PlayerHandler> _mGamesInSession;

        protected void Page_Load(object sender, EventArgs e)
        {
            string style = Request["style"];
            if (!string.IsNullOrEmpty(style))
            {
                if (style == "fluid")
                {
                    styleLink.Href = "fluid.css";
                }
            }

            // We store the game in the Session, but use a dictionary keyed by GUIDs which
            // are stored in the ViewState. This allows the same user in the same browser
            // to open multiple games in different browser tabs.

            _mGamesInSession = (Dictionary<string, PlayerHandler>)Session["Games"];
            if (_mGamesInSession == null)
            {
                _mGamesInSession = new Dictionary<string, PlayerHandler>();
                Session["Games"] = _mGamesInSession;
            }

            var outputBuffersInSession = (Dictionary<string, OutputBuffer>)Session["OutputBuffers"];
            if (outputBuffersInSession == null)
            {
                outputBuffersInSession = new Dictionary<string, OutputBuffer>();
                Session["OutputBuffers"] = outputBuffersInSession;
            }

            _mGameId = (string)ViewState["GameId"];
            if (_mGameId == null)
            {
                _mGameId = Guid.NewGuid().ToString();
                ViewState["GameId"] = _mGameId;
            }

            if (Page.IsPostBack)
            {
                if (_mGamesInSession.ContainsKey(_mGameId))
                {
                    _mPlayer = _mGamesInSession[_mGameId];
                }

                if (!outputBuffersInSession.ContainsKey(_mGameId))
                {
                    // TO DO: Think this only ever happens while debugging?
                    return;
                }
                _mBuffer = outputBuffersInSession[_mGameId];
            }
            else
            {
                _mBuffer = new OutputBuffer();
                outputBuffersInSession.Add(_mGameId, _mBuffer);
            }
        }

        protected void InitTimerTick(object sender, EventArgs e)
        {
            if (_mBuffer == null) return;
            _mBuffer.InitStage++;

            switch (_mBuffer.InitStage)
            {
                case 1:
                    var file = ConfigurationManager.AppSettings["GameName"];
                    var initialText = LoadGame(file);
                    if (_mPlayer == null)
                    {
                        tmrInit.Enabled = false;
                        if (_mPlayer != null) _mPlayer.UpdateGameName("Error loading game");
                    }
                    else
                    {
                        RegisterExternalScripts();
                    }

                    OutputTextNow(initialText);
                    break;
                case 2:
                    tmrInit.Enabled = false;

                    _mPlayer.BeginGame();
                    OutputTextNow(_mPlayer.ClearBuffer());
                    break;
            }
        }

        private string LoadGame(string gameFile)
        {
            if (string.IsNullOrEmpty(gameFile))
            {
                return "No game specified";
            }

            // Block attempts to access files outside the GameFolder
            if (gameFile.Contains(".."))
            {
                return "Invalid filename";
            }

            var rootPath = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["GameFolder"]);
            var libPath = ConfigurationManager.AppSettings["LibraryFolder"];
            var filename = System.IO.Path.Combine(rootPath, gameFile);
            List<string> errors;

            try
            {
                _mPlayer = new PlayerHandler(filename, _mBuffer) {GameId = _mGameId, LibraryFolder = libPath};
                _mGamesInSession[_mGameId] = _mPlayer;
                _mPlayer.BeginWait += m_player_BeginWait;
                _mPlayer.BeginPause += m_player_BeginPause;
                _mPlayer.ShowMenuDelegate = m_player_ShowMenu;
                _mPlayer.ShowQuestionDelegate = m_player_ShowQuestion;
                _mPlayer.AddResource += AddResource;
                _mPlayer.PlayAudio += m_player_PlayAudio;
                _mPlayer.StopAudio += m_player_StopAudio;
                
                if (_mPlayer.Initialise(out errors))
                {
                    // Successful game start
                    return _mPlayer.ClearBuffer();
                }
            }
            catch (Exception ex)
            {
                return "<b>Error loading game:</b><br/>" + ex.Message;
            }

            return errors.Aggregate(string.Empty, (current, error) => current + (error + "<br/>"));
        }

        void RegisterExternalScripts()
        {
            int count = 0;
            foreach (string url in _mPlayer.SetUpExternalScripts())
            {
                count++;
                ScriptManager.RegisterClientScriptInclude(this, this.GetType(), "scriptResource" + count.ToString(), url);
            }
        }

        void m_player_PlayAudio(object sender, PlayerHandler.PlayAudioEventArgs e)
        {
            string functionName = null;
            if (e.Filename.EndsWith(".wav", StringComparison.InvariantCultureIgnoreCase)) functionName = "playWav";
            if (e.Filename.EndsWith(".mp3", StringComparison.InvariantCultureIgnoreCase)) functionName = "playMp3";

            if (functionName == null) return;

            string url = AddResource(e.Filename);
            
            _mBuffer.AddJavaScriptToBuffer(
                functionName,
                new StringParameter(url),
                new BooleanParameter(e.Synchronous),
                new BooleanParameter(e.Looped));
        }

        void m_player_StopAudio()
        {
            _mBuffer.AddJavaScriptToBuffer("stopAudio");
        }

        string AddResource(string filename)
        {
            var resources = Session["Resources"] as SessionResources;
            if (resources == null)
            {
                resources = new SessionResources();
                Session["Resources"] = resources;
            }

            return resources.Add(filename);
        }

        void m_player_BeginWait()
        {
            _mBuffer.AddJavaScriptToBuffer("beginWait");
        }

        void m_player_BeginPause(int ms)
        {
            _mBuffer.AddJavaScriptToBuffer("beginPause", new IntParameter(ms));
        }

        void m_player_ShowMenu(string caption, IDictionary<string, string> options, bool allowCancel)
        {
            _mBuffer.AddJavaScriptToBuffer("showMenu", new StringParameter(caption), new JSONParameter(options), new BooleanParameter(allowCancel));
        }

        void m_player_ShowQuestion(string caption)
        {
            _mBuffer.AddJavaScriptToBuffer("showQuestion", new StringParameter(caption));
        }

        protected void cmdSubmit_Click(object sender, EventArgs e)
        {
            if (_mPlayer == null)
            {
                SessionTimeoutMessage("<b>Sorry, your session has expired and the game has finished.</b><br/><br/>Press your browser's Refresh button to start again.");
                return;
            }

            int tickCount = 0;
            if (fldUITickCount.Value.Length > 0)
            {
                int.TryParse(fldUITickCount.Value, out tickCount);
                fldUITickCount.Value = "";
            }

            if (fldUIMsg.Value.Length > 0)
            {
                string[] args = fldUIMsg.Value.Split(new[] { ' ' }, 2);
                switch (args[0])
                {
                    case "command":
                        _mPlayer.SendCommand(args[1], tickCount);
                        break;
                    case "endwait":
                        _mPlayer.EndWait();
                        break;
                    case "endpause":
                        _mPlayer.EndPause();
                        break;
                    case "choice":
                        _mPlayer.SetMenuResponse(args[1]);
                        break;
                    case "choicecancel":
                        _mPlayer.CancelMenu();
                        break;
                    case "msgbox":
                        _mPlayer.SetQuestionResponse(args[1]);
                        break;
                    case "event":
                        SendEvent(args[1]);
                        break;
                    case "tick":
                        _mPlayer.Tick(tickCount);
                        break;

                }
                fldUIMsg.Value = "";
            }

            var output = _mPlayer.ClearBuffer();
            _mBuffer.OutputText(output);
            ClearJavaScriptBuffer();
        }

        void OutputTextNow(string text)
        {
            _mBuffer.OutputText(text);
            ClearJavaScriptBuffer();
        }

        void SessionTimeoutMessage(string text)
        {
            _mBuffer = new OutputBuffer();
            _mBuffer.OutputText(text);
            _mBuffer.AddJavaScriptToBuffer("sessionTimeout");
            ClearJavaScriptBuffer();
        }

        void ClearJavaScriptBuffer()
        {
            var count = 0;
            foreach (var javaScript in _mBuffer.ClearJavaScriptBuffer())
            {
                ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "script" + count.ToString(), javaScript, true);
                count++;
            }
        }

        void SendEvent(string data)
        {
            var args = data.Split(new[] { ';' }, 2);
            _mPlayer.SendEvent(args[0], args[1]);
        }
    }
}